<template>
  <div class="va-df-input-control va-df-image-upload">
    <div class="va-df-image-upload-list">
      <draggable v-model="files">
        <div class="__image-item"
            v-for="file in files" :key="file.raw && file.raw.uid || file.fullUrl"
            tabindex="0"
            @keydown.delete="removeFile(file)"
            @keydown.enter="checkFile(file)">
          <div class="va-df-image-upload-thumb">
            <div class="__image-img" v-if="file.fullUrl && !file.raw">
              <img :src="file.fullUrl" v-if="urlIsImage(file.fullUrl)">
              <span v-else>{{file.fullUrl}}</span>
            </div>
            <template v-if="file.raw">
              <canvas ref="canvas" v-if="fileIsImage(file.raw)"></canvas>
              <span v-else>{{file.raw.name}}</span>
            </template>

            <div class="va-df-image-upload-thumb-cover"
                 v-if="file.status === 'ready' || file.status === 'uploading'"
                 :style="{height: `${100 - (file.percentage || 0)}%`}"></div>
            <div class="va-df-image-upload-state" v-if="file.status !== 'success'">{{getState(file)}}</div>
            <div class="va-df-image-upload-operate">
              <button type="button" class="btn btn-xs btn-primary"
                      v-show="!disabled"
                      @click="checkFile(file)"
                      :disabled="!file.fullUrl"><i class="va-icon-eye"></i></button>
              <button type="button" class="btn btn-xs btn-danger"
                      @click="removeFile(file)"><i class="va-icon-trash-o"></i></button>
            </div>
          </div>
        </div>
      </draggable>
      <div class="__image-item va-df-image-upload-add" v-show="!disabled && !(files.length > 0 && field.isSingleFile)" @click="onClick" tabindex="0" @keyup.enter="onClick">
        <i class="va-icon-plus"></i>
        <input ref="input" style="display: none;" type="file" @change="onChange" :accept="field.accept">
      </div>
    </div>
  </div>
</template>

<script>
  import _ from 'lodash'
  import draggable from '../../../libs/draggable/draggable'
  import ajax from './ajax'

  let helper = {
    support: !!(window.FileReader && window.CanvasRenderingContext2D),
    isImage: function (file) {
      let type = '|' + file.type.slice(file.type.lastIndexOf('/') + 1) + '|'
      return '|jpg|png|jpeg|bmp|gif|'.indexOf(type) !== -1
    }
  }

  export default {
    components: {draggable},
    props: {
      value: {},
      field: {
        type: Object,
        default () {
          return {}
        }
      },
      disabled: Boolean
    },
    data () {
      return {
        files: [],
        modelValue: null,
        reqs: {},
        tempIndex: 1,
        uploadCount: 0,
        thumbSupport: helper.support
      }
    },
    mounted () {
      this.initValue()
    },
    methods: {
      urlIsImage (url) {
        return /(jpg|png|jpeg|bmp|fig)$/.test(url.toLowerCase())
      },
      getUrlExtension (url) {
        return (url && url.split('.').pop().toUpperCase()) || ''
      },
      fileIsImage: helper.isImage,
      getState (file) {
        if (file.status === 'ready') {
          return '待上传'
        } else if (file.status === 'uploading') {
          return `已上传${Math.floor(file.percentage)}%`
        }
        return ''
      },
      checkFile (file) {
        if (file.fullUrl) {
          window.open(file.fullUrl, '_blank')
        }
      },
      onClick (evt) {
        if (!this.disabled && evt.target !== this.$refs.input) {
          this.$refs.input.value = null
          this.$refs.input.click()
        }
      },
      onChange (evt) {
        const files = evt.target.files
        if (!files) return
        this.uploadFiles(files)
      },
      uploadFiles (files) {
        let thumbWidth = this.field.thumbWidth || 100
        let thumbHeight = this.field.thumbHeight || 100

        let postFiles = Array.prototype.slice.call(files)
        if (this.field.isSingleFile) { postFiles = postFiles.slice(0, 1) }
        if (postFiles.length === 0) { return }
        _.forEach(postFiles, rawFile => {
          rawFile.uid = Date.now() + this.tempIndex++
          let file = {
            status: 'ready',
            name: rawFile.name,
            size: rawFile.size,
            percentage: 0,
            uid: rawFile.uid,
            raw: rawFile
          }

          if (this.thumbSupport && this.fileIsImage(rawFile)) {
            let reader = new FileReader()
            reader.onload = evt => {
              let img = new Image()
              img.onload = () => {
                let scale1 = thumbWidth / thumbHeight
                let scale2 = img.width / img.height
                let imgWidth, imgHeight
                if (scale2 > scale1) {
                  imgWidth = thumbWidth
                  imgHeight = imgWidth / scale2
                } else {
                  imgHeight = thumbHeight
                  imgWidth = imgHeight * scale2
                }
                let canvas = this.$refs.canvas[_.indexOf(this.files, file)]
                canvas.width = thumbWidth
                canvas.height = thumbHeight
                canvas.getContext('2d').drawImage(img, (thumbWidth - imgWidth) / 2, (thumbHeight - imgHeight) / 2, imgWidth, imgHeight)
              }
              img.src = evt.target.result
            }
            reader.readAsDataURL(rawFile)
          }

          try {
            file.url = URL.createObjectURL(rawFile)
          } catch (err) {
            console.error(err)
            return
          }
          this.files.push(file)
          if (this.field.autoUpload !== false) this.upload(file)
        })
      },
      upload (file) {
        this.$refs.input.value = null

        if (!this.field.beforeUpload) {
          return this.post(file)
        }

        const before = this.field.beforeUpload(file)
        if (before && before.then) {
          before.then(processedFile => {
            if (Object.prototype.toString.call(processedFile) === '[object File]') {
              this.post(processedFile)
            } else {
              this.post(file)
            }
          }, () => {
            this.removeFile(file)
          })
        } else if (before !== false) {
          this.post(file)
        } else {
          this.removeFile(file)
        }
      },
      abort (file) {
        const { reqs } = this
        if (file) {
          let uid = file
          if (file.uid) uid = file.uid
          if (reqs[uid]) {
            reqs[uid].abort()
          }
        } else {
          Object.keys(reqs).forEach((uid) => {
            if (reqs[uid]) reqs[uid].abort()
            delete reqs[uid]
          })
        }
      },
      removeFile (file) {
        if (this.disabled) return
        if (file.raw) {
          this.abort(file)
        }
        let idx = _.indexOf(this.files, file)
        if (idx > -1) {
          this.files.splice(idx, 1)
          this.files = [...this.files]
        }
      },
      post (file) {
        let rawFile = file.raw
        const errorHandler = err => {
          file.status = 'fail'
          let idx = _.indexOf(this.files, file)
          if (idx > -1) {
            this.files.splice(idx, 1)
            this.files = [...this.files]
          }
          this.field.onFileUploadError && this.field.onFileUploadError(file, err)
          delete this.reqs[uid]
          this.uploadCount--
        }
        const { uid } = rawFile
        const options = {
          headers: (this.field.getFileUploadHeaders && this.field.getFileUploadHeaders()) || {},
          withCredentials: false,
          file: rawFile,
          filename: 'file',
          data: (this.field.getFileUploadData && this.field.getFileUploadData()) || {},
          action: this.field.fileUploadFullUrl,
          onProgress: e => {
            file.status = 'uploading'
            file.percentage = e.percent || 0
            this.field.onFileUploadProcess && this.field.onFileUploadProcess(file, e)
          },
          onSuccess: res => {
            file.status = 'success'
            file.response = res
            delete this.reqs[uid]
            let imgUrl = this.field.fileUploadSetImgUrlByResp && this.field.fileUploadSetImgUrlByResp(file, res)
            if (imgUrl === false) {
              errorHandler(res)
              return
            } else if (imgUrl && _.isString(imgUrl)) {
              file.fullUrl = imgUrl
            }
            this.field.onFileUploadSuccess && this.field.onFileUploadSuccess(file, res)
            this.uploadCount--

            this.updateModel()
          },
          onError: errorHandler
        }
        this.uploadCount++
        const req = ajax(options)
        this.reqs[uid] = req
        if (req && req.then) {
          req.then(options.onSuccess, options.onError)
        }
      },
      updateModel () {
        let tmp = []
        _.forEach(this.files, file => {
          if (file.fullUrl) {
            tmp.push(file.fullUrl)
          }
        })
        if (this.field.isSingleFile) {
          tmp = tmp.length > 0 ? tmp[0] : null
        }
        this.modelValue = tmp
      },
      initValue () {
        if (!_.isEqual(this.value, this.modelValue)) {
          let val = (_.isString(this.value) ? _.filter(this.value.split(','), e => !!e) : this.value) || []
          _.forEach(this.files, file => {
            if (file.raw) {
              this.abort(file)
            }
          })
          if (this.field.isSingleFile) {
            if (val.length > 0) {
              this.files = [{fullUrl: val[0]}]
            } else {
              this.files = []
            }
          } else {
            this.files = _.map(val, item => { return {fullUrl: item} })
          }
          this.modelValue = this.value
        }
      }
    },
    watch: {
      uploadCount () {
        this.$emit('upload', this.uploadCount > 0)
      },
      value () {
        this.initValue()
      },
      modelValue () {
        this.$emit('input', this.modelValue)
      },
      files () {
        this.updateModel()
      }
    }
  }
</script>
